package com.codemes.happylist.modules.music;

import cn.hutool.core.date.BetweenFormater;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.json.JSONUtil;
import com.codemes.happylist.utils.CacheUtil;
import com.codemes.happylist.utils.MediaUtil;
import de.felixroske.jfxsupport.FXMLController;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.media.*;
import javafx.stage.DirectoryChooser;
import javafx.stage.FileChooser;
import javafx.util.Callback;
import javafx.util.Duration;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.ObjectUtils;

import java.io.File;
import java.net.URL;
import java.util.*;

/**
 * 音乐播放器
 * @author nscoffee
 */
@FXMLController
@Slf4j
public class MusicPlayerMainController implements Initializable {
    @FXML
    public Button btnChooseDir;
    @FXML
    public Label lblChooseDir;
    @FXML
    public BorderPane container;
    @FXML
    public Label lblMusicCount;
    @FXML
    public VBox vbInitMain;
    @FXML
    public Button btnPrev;
    @FXML
    public Button btnPlayer;
    @FXML
    public Button btnNext;
    @FXML
    public Label lblTimeCur;
    @FXML
    public Label lblTimeTotal;
    @FXML
    public Button btnVoice;
    @FXML
    public Slider sliderSound;
    @FXML
    public Button btnPlayModel;
    @FXML
    public Slider sliderMusic;
    @FXML
    public Label lblOpenFile;
    @FXML
    public Label lblRefreshDir;
    @FXML
    public Label lblPlayAll;
    @FXML
    public Label lblCurMusicInfo;

    HBox musicBox;
    TableView<MusicFile> tableView;

    private Boolean sliderMusicCanMove = false;
    private MediaPlayer mediaPlayer = null;
    private final MusicPlayList musicPlayList = new MusicPlayList(new ArrayList<>());
    private static final String PAUSE_CLASS = "btn-pausing";
    private static final String PLAYING_CLASS = "btn-playing";
    private static final Double DEFAULT_VOLUME=20.66d;
    private static final String[] AUDIO_FILE_TYPES = {"*.mp3","*.wav","*.mid","*.wma","*.aiff","*.aif","*.ra","*.vqf","*.ape","*.cda"};

    private static final String MUSIC_TABLE_LIST_CACHE_KEY="MUSIC_TABLE_LIST_CACHE_KEY";
    private static final String DIRECTORY_CHOOSE_PREV_INFO = "DIRECTORY_CHOOSE_PREV_INFO";
    private static final String PLAY_MODEL_CACHE_kEY = "PLAY_MODEL_CACHE_kEY";

    @Override
    public void initialize(URL url, ResourceBundle resourceBundle) {
        sliderSound.setValue(DEFAULT_VOLUME);
        initMusicBox();
        // 设置初始音乐
        String resource = Objects.requireNonNull(MusicPlayerMainController.class.getResource("/fxml/music/magic.mp3")).toExternalForm();
        mediaPlayer = playMusic(resource,"欢迎曲： magic");

        // 打开音乐文件，并播放
        lblOpenFile.setOnMouseClicked(e -> {
            String fileChooseRecordKey = "FileChoosePrevInfo";

            String chooseDir = CacheUtil.get(fileChooseRecordKey);
            FileChooser fileChooser = new FileChooser();
            if(!ObjectUtils.isEmpty(chooseDir)){
                fileChooser.setInitialDirectory(new File(chooseDir));
            }
            fileChooser.setSelectedExtensionFilter(new FileChooser.ExtensionFilter("请选择音频文件", AUDIO_FILE_TYPES));
            File file = fileChooser.showOpenDialog(lblOpenFile.getScene().getWindow());
            if(null==file){
                return;
            }
            chooseDir = file.getParent();
            CacheUtil.set(fileChooseRecordKey,chooseDir);
            MusicFile musicFile = MediaUtil.extract(file);
            musicPlayList.addAndPlay(musicFile);
            mediaPlayer = playMusic(musicFile);
        });

        String cachedMusicList = CacheUtil.get(MUSIC_TABLE_LIST_CACHE_KEY);
        if(!ObjectUtils.isEmpty(cachedMusicList)){
            List<MusicFile> musicFiles = JSONUtil.toList(JSONUtil.parseArray(cachedMusicList),MusicFile.class);
            showMusicTableList(musicFiles);
        }
        // 选择目录，并扫描音频文件
        lblChooseDir.setOnMouseClicked(e -> {
            openDirAndScanMusicFiles();
        });

        // 重新扫描目录下的音频文件
        lblRefreshDir.setOnMouseClicked(e -> {
            String chooseDir = CacheUtil.get(DIRECTORY_CHOOSE_PREV_INFO);
            if(ObjectUtils.isEmpty(chooseDir)){
                return;
            }
            Platform.runLater(() -> {
                // 扫描目录下音乐文件
                List<MusicFile> musicFiles = scanMusicFile(new File(chooseDir));
                CacheUtil.set(MUSIC_TABLE_LIST_CACHE_KEY, JSONUtil.toJsonStr(musicFiles));
                showMusicTableList(musicFiles);
                musicPlayList.setPlayList(musicFiles);
            });
        });

        // 打开文件夹
        btnChooseDir.setOnAction(e -> {
            openDirAndScanMusicFiles();
        });

        lblPlayAll.setOnMouseClicked(e -> {
            List<MusicFile> musicFiles = tableView.getItems();
            musicPlayList.newPlayList(musicFiles);
            MediaPlayerModel curModel = (MediaPlayerModel) btnPlayModel.getUserData();
            MusicFile musicFile = musicPlayList.chooseMusicFile(curModel);
            playMusic(musicFile);
        });

        String modelIndex = CacheUtil.get(PLAY_MODEL_CACHE_kEY);
        MediaPlayerModel defaultPlayModel = MediaPlayerModel.MODEL_LOOP;
        if(!ObjectUtils.isEmpty(modelIndex)){
            defaultPlayModel = MediaPlayerModel.getByIndex(Integer.valueOf(modelIndex));
        }
        btnPlayModel.getStyleClass().add(defaultPlayModel.getClassName());
        Tooltip tooltip = new Tooltip(defaultPlayModel.getModelName());
        btnPlayModel.setTooltip(tooltip);
        btnPlayModel.setUserData(defaultPlayModel);
        btnPlayModel.setOnAction(e -> {
            MediaPlayerModel curModel = (MediaPlayerModel) btnPlayModel.getUserData();
            MediaPlayerModel nextModel = MediaPlayerModel.nextModel(curModel);
            log.info("curModel -> {} nextModel->{}",curModel,nextModel);
            btnPlayModel.setUserData(nextModel);
            btnPlayModel.getStyleClass().remove(curModel.getClassName());
            btnPlayModel.getStyleClass().add(nextModel.getClassName());
            tooltip.setText(nextModel.getModelName());
            CacheUtil.set(PLAY_MODEL_CACHE_kEY,nextModel.getIndex().toString());
        });

        btnNext.setOnAction(e -> {
            MediaPlayerModel curModel = (MediaPlayerModel) btnPlayModel.getUserData();
            MusicFile musicFile = musicPlayList.chooseMusicFile(curModel);
            if(curModel.equals(MediaPlayerModel.MODEL_SINGLE)){
                musicFile = musicPlayList.listLoop();
            }
            playMusic(musicFile);
            tableView.getSelectionModel().select(musicFile);
            tableView.scrollTo(musicFile);
        });

        btnPrev.setOnAction(e->{
            MusicFile musicFile = musicPlayList.prev();
            playMusic(musicFile);
            tableView.getSelectionModel().select(musicFile);
            tableView.scrollTo(musicFile);
        });

        sliderMusic.setOnMousePressed( e -> {
            log.info("setOnMousePressed");
            sliderMusicCanMove = true;
        });
        sliderMusic.setOnMouseDragged( e -> {
            double startTime = sliderMusic.getValue();
            log.info("setOnMouseDragged startTime -> {}",startTime);
            lblTimeCur.setText(formatMusicTime((long) startTime*1000));
            sliderMusicCanMove = true;
        });
        sliderMusic.setOnMouseReleased(e -> {
            log.info("setOnMouseReleased");
            double startTime = sliderMusic.getValue();
            Duration newStartTime = Duration.seconds(startTime);
            log.info("mediaPlayer new start time ->{}",newStartTime);
            mediaPlayer.seek(newStartTime);
            sliderMusicCanMove = false;
        });
    }

    private void openDirAndScanMusicFiles() {
        String chooseDir = CacheUtil.get(DIRECTORY_CHOOSE_PREV_INFO);
        DirectoryChooser directoryChooser = new DirectoryChooser();
        if(!ObjectUtils.isEmpty(chooseDir)){
            directoryChooser.setInitialDirectory(new File(chooseDir));
        }
        File dir = directoryChooser.showDialog(container.getScene().getWindow());
        if(null==dir){
            return;
        }
        chooseDir = dir.getAbsolutePath();
        CacheUtil.set(DIRECTORY_CHOOSE_PREV_INFO,chooseDir);
        Platform.runLater(() -> {
            // 扫描目录下音乐文件
            List<MusicFile> musicFiles = scanMusicFile(dir);
            CacheUtil.set(MUSIC_TABLE_LIST_CACHE_KEY, JSONUtil.toJsonStr(musicFiles));
            showMusicTableList(musicFiles);
        });
    }

    private void showMusicTableList(List<MusicFile> musicFiles) {
        lblMusicCount.setText(musicFiles.size()+" 首音乐,");
        vbInitMain.setVisible(false);
        vbInitMain.setManaged(false);
        container.setCenter(musicBox);

        tableView.getItems().clear();
        tableView.getItems().addAll(musicFiles);
    }

    private void initMusicBox(){
        musicBox = new HBox();
        tableView = new TableView<>();
        tableView.prefWidthProperty().bind(musicBox.widthProperty());
        musicBox.getChildren().add(tableView);

        TableColumn<MusicFile,String> colId = new TableColumn<>("");
        colId.prefWidthProperty().bind(tableView.widthProperty().multiply(0.05));
        colId.setCellFactory(new Callback<>() {
            @Override
            public TableCell<MusicFile, String> call(TableColumn<MusicFile, String> musicFileStringTableColumn) {
                return new TableCell<>() {
                    @Override
                    protected void updateItem(String s, boolean b) {
                        super.updateItem(s, b);
                        this.setText(null);
                        this.setGraphic(null);
                        this.setText(String.valueOf(this.getIndex() + 1));
                    }
                };
            }
        });

        TableColumn<MusicFile,String> colTitle = new TableColumn<>("音乐标题");
        colTitle.prefWidthProperty().bind(tableView.widthProperty().multiply(0.3));
        colTitle.setCellValueFactory(new PropertyValueFactory<>("title"));

        TableColumn<MusicFile,String> colSinger = new TableColumn<>("歌手");
        colSinger.setCellValueFactory(new PropertyValueFactory<>("singer"));
        colSinger.prefWidthProperty().bind(tableView.widthProperty().multiply(0.2));

        TableColumn<MusicFile,String> colAlbum = new TableColumn<>("专辑");
        colAlbum.setCellValueFactory(new PropertyValueFactory<>("album"));
        colAlbum.prefWidthProperty().bind(tableView.widthProperty().multiply(0.3));

        TableColumn<MusicFile,String> colTime = new TableColumn<>("时长");
        colTime.setCellValueFactory(new PropertyValueFactory<>("duration"));
        colTime.prefWidthProperty().bind(tableView.widthProperty().multiply(0.1));

        TableColumn<MusicFile,String> colOther = new TableColumn<>("");
        tableView.getColumns().addAll(colId,colTitle,colSinger,colAlbum,colTime,colOther);


        TableView.TableViewSelectionModel<MusicFile> selectionModel = tableView.getSelectionModel();
        selectionModel.setSelectionMode(SelectionMode.SINGLE);

        tableView.setOnMouseClicked(me -> {
            if(me.getClickCount() == 2){
                log.info("双击一行记录 -> {}", me.getTarget());
                MusicFile musicFile = tableView.getSelectionModel().getSelectedItem();
                musicPlayList.addAndPlay(musicFile);
                playMusic(musicFile);
            }
        });
    }

    private List<MusicFile> scanMusicFile(File file){
        List<MusicFile> musicFiles = new ArrayList<>();
        if(file.isDirectory()){
            File[] subFileList = file.listFiles(subFile -> {
                if(subFile.isDirectory()){
                    return true;
                }
                String ext = FileUtil.extName(subFile);
                if(ObjectUtils.isEmpty(ext)){
                    return false;
                }
                ext = "*."+ext;
                return ArrayUtil.contains(AUDIO_FILE_TYPES, ext);
            });
            if(subFileList!=null && subFileList.length>0){
                for (File subFile : subFileList) {
                    List<MusicFile> subMusicFiles = scanMusicFile(subFile);
                    musicFiles.addAll(subMusicFiles);
                }
            }
        }else {
            MusicFile musicFile = MediaUtil.extract(file);
            musicFiles.add(musicFile);
        }
        return musicFiles;
    }

    private MediaPlayer playMusic(MusicFile musicFile) {
        if(null==musicFile){
            return null;
        }
        String musicSource = URLUtil.getURL(new File(musicFile.getAbsPath())).toExternalForm();
        tableView.getSelectionModel().select(musicFile);
        return playMusic(musicSource,musicFile.getTitle()+" - "+musicFile.getSinger()+" - "+musicFile.getAlbum());
    }

    private MediaPlayer playMusic(String resource,String desc) {
        if(null!= mediaPlayer){
            mediaPlayer.dispose();
        }
        log.info("play music file url path -> {}",resource);
        Media media = new Media(resource);
        media.setOnError(() -> log.info("media error -> {}",media.getError()));
        mediaPlayer = new MediaPlayer(media);
        initMediaPlayListener(mediaPlayer);
        initBtnPlayListener(mediaPlayer);
        initSliderSoundListener(mediaPlayer);
        mediaPlayer.play();
        lblCurMusicInfo.setText("当前播放："+desc);
        return mediaPlayer;
    }

    private void initBtnPlayListener(MediaPlayer mediaPlayer){
        btnPlayer.setOnAction(e -> {
            MediaPlayer.Status status = mediaPlayer.getStatus();
            if(status.equals(MediaPlayer.Status.PLAYING)){
                mediaPlayer.pause();
            }else{
                mediaPlayer.play();
            }
        });
    }

    private void initSliderSoundListener(MediaPlayer mediaPlayer){
        double curSound = sliderSound.getValue();
        mediaPlayer.setVolume(curSound*0.01);
        sliderSound.valueProperty().addListener((observableValue, number, t1) -> {
            mediaPlayer.setVolume(t1.doubleValue()*0.01);
        });
    }

    private void initMediaPlayListener(MediaPlayer mediaPlayer){
        mediaPlayer.setOnEndOfMedia(() -> {
            log.info("播放完成事件...........");
            MediaPlayerModel curModel = (MediaPlayerModel) btnPlayModel.getUserData();
            MusicFile musicFile = musicPlayList.chooseMusicFile(curModel);
            playMusic(musicFile);
            tableView.getSelectionModel().select(musicFile);
            tableView.scrollTo(musicFile);
        });
        mediaPlayer.currentCountProperty().addListener((observableValue, number, t1) -> log.info("currentCountProperty value->{} t1->{} number->{} ",observableValue,t1,number));
        mediaPlayer.statusProperty().addListener((observableValue, status, t1) -> {
            // 改变播放按钮事件
            changeBtnPlayStatus(t1);
        });

        mediaPlayer.currentTimeProperty().addListener((observableValue, duration, t1) -> {
            Duration total = mediaPlayer.getTotalDuration();
            String totalStr = formatMusicTime((long) total.toMillis());
            lblTimeTotal.setText(totalStr);
            sliderMusic.setMax(total.toSeconds());

            if(Boolean.FALSE.equals(sliderMusicCanMove)){
                sliderMusic.setValue(t1.toSeconds());
                lblTimeCur.setText(formatMusicTime((long) t1.toMillis()));
            }
        });
    }

    private void changeBtnPlayStatus(MediaPlayer.Status t1) {
        if(t1.equals(MediaPlayer.Status.PLAYING)){
            btnPlayer.getStyleClass().remove(PAUSE_CLASS);
            if(btnPlayer.getStyleClass().contains(PLAYING_CLASS)){
                return;
            }
            btnPlayer.getStyleClass().add(PLAYING_CLASS);
        }else{
            btnPlayer.getStyleClass().remove(PLAYING_CLASS);
            if(btnPlayer.getStyleClass().contains(PAUSE_CLASS)){
                return;
            }
            btnPlayer.getStyleClass().add(PAUSE_CLASS);
        }
    }

    private String formatMusicTime(long millis) {
        String totalStr = DateUtil.formatBetween(millis, BetweenFormater.Level.SECOND);
        totalStr = totalStr.replace("小时",":");
        totalStr = totalStr.replace("分",":");
        totalStr = totalStr.replace("秒","");
        if(!totalStr.contains(":")){
            totalStr = "00:"+totalStr;
        }
        return totalStr;
    }
}
